home *** CD-ROM | disk | FTP | other *** search
- /*
-
- Copyright (C) 1995
- Free Software Foundation, Inc.
-
- This file is part of GNU cfengine - written and maintained
- by Mark Burgess, Dept of Computing and Engineering, Oslo College,
- Dept. of Theoretical physics, University of Oslo
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
-
- */
-
- /*******************************************************************/
- /* */
- /* File Image copying */
- /* */
- /* client part for remote copying */
- /* */
- /*******************************************************************/
-
- #define INET 1
-
- #include "cf.defs.h"
- #include "cf.extern.h"
-
- /* Protocol specs:
-
- Pre authorization: CAUTH (clear text auth) client-name server-name user-name
-
- Stat file: SYNCH long-time-string STAT filename
- reply with OK: <stat-reply-string>
- keep open channel
-
- md5 file Checksum compare
-
- Get file: GET filename
-
- reply with <multiple buffers>, break on zero length buffer
- or BAD: <message>
- keep open channel for more stat/get
-
- Opendir: OPENDIR dir-name
-
- reply with <multiple buffers>, break on zero length buffer
-
- Exec: EXEC option-string
- reply with <multiple buffers>, break on zero length buffer
- Close after this.
-
- Replies:
-
- OK: message (succeeded)
-
- BAD: message (failed)
-
- */
-
- /*********************************************************************/
- /* Level 1 */
- /*********************************************************************/
-
- OpenServerConnection(serverlist,ip)
-
- struct Item *serverlist;
- struct Image *ip;
-
- { int TimeOut(),err;
- struct sockaddr_in cin;
-
- if (strcmp(ip->server,"localhost") == 0)
- {
- AUTHENTICATED = true;
- return true;
- }
-
- AUTHENTICATED = false;
-
- if (SD == cf_not_connected)
- {
- Debug("Opening server connnection to %s\n",ip->server);
- bzero(&cin,sizeof(cin));
-
- cin.sin_port = PORTNUMBER;
- cin.sin_addr.s_addr = (ip->dns)->s_addr;
- cin.sin_family = AF_INET;
-
- Debug("Trying to connect to %s = %s, port h=%d\n",ip->server,inet_ntoa(cin.sin_addr),PORTNUMBER);
-
- if ((SD = socket(AF_INET,SOCK_STREAM,0)) == -1)
- {
- CfLog(cferror,"","socket");
- AUTHENTICATED = false;
- return false;
- }
-
- signal(SIGALRM,(void *)TimeOut);
- alarm(CF_TIMEOUT);
-
- if (err=connect(SD,(void *) &cin,sizeof(cin)) == -1)
- {
- sprintf(OUTPUT,"Couldn't connect to host %s\n",ip->server);
- CfLog(cfinform,OUTPUT,"connect");
- CloseServerConnection();
- AUTHENTICATED = false;
- return false;
- }
-
- alarm(0);
- signal(SIGALRM,SIG_DFL);
-
- Debug("Sending on socket...%d\n",SD);
-
- if (! IdentifyForVerification(SD,VFQNAME))
- {
- CfLog(cferror,"Server registration procedure failed","");
- errno = EPERM;
- CloseServerConnection();
- AUTHENTICATED = false;
- return false;
- }
-
- AUTHENTICATED = true;
- return true;
- }
- else
- {
- Debug("Server connection to %s already open on %d\n",ip->server,SD);
- }
-
- AUTHENTICATED = true;
- return true;
- }
-
- /*********************************************************************/
-
- CloseServerConnection()
-
- {
- Debug("Closing current connection\n");
- close(SD);
- SD = cf_not_connected;
- }
-
- /*********************************************************************/
-
- cf_rstat(file,buf,ip,stattype)
-
- /* If a link, this reads readlink and sends it back in the same
- package. It then caches the value for each copy command */
-
- char *file;
- struct stat *buf;
- struct Image *ip;
- char *stattype;
-
- { char sendbuffer[bufsize];
- char recvbuffer[bufsize];
- char in[bufsize],out[bufsize];
- struct cfstat cfst;
- int i,ret;
- time_t tloc;
- struct hostent *hp;
-
- Debug("cf_rstat(%s)\n",file);
-
- if (strlen(file) > bufsize-30)
- {
- CfLog(cferror,"Filename too long","");
- return -1;
- }
-
- ret = GetCachedStatData(file,buf,ip,stattype);
-
- if (ret != 0)
- {
- return ret;
- }
-
- if ((tloc = time((time_t *)NULL)) == -1)
- {
- CfLog(cferror,"Couldn't read system clock\n","");
- }
-
- sendbuffer[0] = '\0';
-
- if (ip->secure)
- {
- #ifdef HAVE_LIBCRYPTO
- bzero(in,bufsize);
- bzero(out,bufsize);
- bzero(sendbuffer,bufsize);
- sprintf(in,"SYNCH %d STAT %s",tloc,file);
- cfencrypt(in,out,CFDES1,CFDES2,CFDES3,strlen(in));
- sprintf(sendbuffer,"SSYNCH %d",strlen(in));
- bcopy(out,sendbuffer+23,bufsize-24);
- #endif
- }
- else
- {
- sprintf(sendbuffer,"SYNCH %d STAT %s",tloc,file);
- }
-
- if (send(SD,sendbuffer,bufsize,0) == -1)
- {
- CfLog(cferror,"Can't send on socket","send");
- return -1;
- }
-
- if (RecvSocketStream(SD,recvbuffer,bufsize,0) == -1)
- {
- return -1;
- }
-
- if (strstr(recvbuffer,"unsynchronized"))
- {
- CfLog(cferror,"Clocks differ too much to do copy by date (security)","");
- CfLog(cferror,recvbuffer+4,"");
- return -1;
- }
-
- if (strncmp(recvbuffer,"BAD",3) == 0)
- {
- sprintf(OUTPUT,"Server returned error: %s\n",recvbuffer+4);
- CfLog(cfverbose,OUTPUT,"");
- errno = EPERM;
- return -1;
- }
-
- if (strncmp(recvbuffer,"OK:",3) == 0)
- {
- int d1,d2,d3,d4,d5,d6,d7,d8,d9,d10,d11,d12=0;
-
- sscanf(recvbuffer,"OK: %1d %5d %12d %12d %12d %12d %12d %12d %12d %12d %12d %12d",
- &d1,&d2,&d3,&d4,&d5,&d6,&d7,&d8,&d9,&d10,&d11,&d12);
-
- cfst.cf_type = (enum cf_filetype) d1;
- cfst.cf_mode = (mode_t) d2;
- cfst.cf_lmode = (mode_t) d3;
- cfst.cf_uid = (uid_t) d4;
- cfst.cf_gid = (gid_t) d5;
- cfst.cf_size = (off_t) d6;
- cfst.cf_atime = (time_t) d7;
- cfst.cf_mtime = (time_t) d8;
- cfst.cf_ctime = (time_t) d9;
- cfst.cf_makeholes = (char) d10;
- ip->makeholes = (char) d10;
- cfst.cf_ino = d11;
- cfst.cf_nlink = d12;
-
- /* Use %?d here to avoid memory overflow attacks */
-
- Debug("Mode = %d,%d\n",d2,d3);
-
- Debug("OK: type=%d\n mode=%o\n lmode=%o\n uid=%d\n gid=%d\n size=%ld\n atime=%d\n mtime=%d ino=%d nlnk=%d\n",
- cfst.cf_type,cfst.cf_mode,cfst.cf_lmode,cfst.cf_uid,cfst.cf_gid,(long)cfst.cf_size,
- cfst.cf_atime,cfst.cf_mtime,cfst.cf_ino,cfst.cf_nlink);
-
- bzero(recvbuffer,bufsize);
-
- if (RecvSocketStream(SD,recvbuffer,bufsize,0) == -1)
- {
- return -1;
- }
-
- Debug("Linkbuffer: %s\n",recvbuffer);
-
- if (strlen(recvbuffer) > 3)
- {
- cfst.cf_readlink = strdup(recvbuffer+3);
- }
- else
- {
- cfst.cf_readlink = NULL;
- }
-
- switch (cfst.cf_type)
- {
- case cf_reg: cfst.cf_mode |= (mode_t) S_IFREG;
- break;
- case cf_dir: cfst.cf_mode |= (mode_t) S_IFDIR;
- break;
- case cf_char: cfst.cf_mode |= (mode_t) S_IFCHR;
- break;
- case cf_fifo: cfst.cf_mode |= (mode_t) S_IFIFO;
- break;
- case cf_sock: cfst.cf_mode |= (mode_t) S_IFSOCK;
- break;
- case cf_block: cfst.cf_mode |= (mode_t) S_IFBLK;
- break;
- }
-
-
- cfst.cf_filename = strdup(file);
- cfst.cf_server = strdup(ip->server);
-
- if ((cfst.cf_filename == NULL) ||(cfst.cf_server) == NULL)
- {
- FatalError("Memory allocation in cf_rstat");
- }
-
- cfst.cf_failed = false;
-
- if (cfst.cf_lmode != 0)
- {
- cfst.cf_lmode |= (mode_t) S_IFLNK;
- }
-
- CacheData(&cfst,ip);
-
- if ((cfst.cf_lmode != 0) && (strcmp(stattype,"link") == 0))
- {
- buf->st_mode = cfst.cf_lmode;
- }
- else
- {
- buf->st_mode = cfst.cf_mode;
- }
-
- buf->st_uid = cfst.cf_uid;
- buf->st_gid = cfst.cf_gid;
- buf->st_size = cfst.cf_size;
- buf->st_mtime = cfst.cf_mtime;
- buf->st_ctime = cfst.cf_ctime;
- buf->st_atime = cfst.cf_atime;
- buf->st_ino = cfst.cf_ino;
- buf->st_nlink = cfst.cf_nlink;
-
- return 0;
- }
-
-
- sprintf(OUTPUT,"Protocol error in stat (shouldn't happen). Got [%s]\n",recvbuffer);
- CfLog(cfinform,OUTPUT,"");
- errno = EPERM;
-
- return -1;
- }
-
- /*********************************************************************/
-
- CFDIR *cf_ropendir(dirname,ip)
-
- char *dirname;
- struct Image *ip;
-
- { char sendbuffer[bufsize];
- char recvbuffer[bufsize];
- int i,offset,n, done=false;
- CFDIR *cfdirh;
- char *sp;
-
- Debug("CfOpenDir(%s:%s)\n",ip->server,dirname);
-
- if (strlen(dirname) > bufsize - 20)
- {
- CfLog(cferror,"Directory name too long");
- return NULL;
- }
-
- if ((cfdirh = (CFDIR *)malloc(sizeof(CFDIR))) == NULL)
- {
- CfLog(cferror,"Couldn't allocate memory in cf_ropendir\n","");
- exit(1);
- }
-
- cfdirh->cf_list = NULL;
- cfdirh->cf_listpos = NULL;
- cfdirh->cf_dirh = NULL;
-
- sprintf(sendbuffer,"OPENDIR %s",dirname);
-
- if (send(SD,sendbuffer,bufsize,0) == -1)
- {
- return NULL;
- }
-
- while (!done)
- {
- if ((n = RecvSocketStream(SD,recvbuffer,bufsize,0)) == -1)
- {
- if (errno == EINTR)
- {
- continue;
- }
- return false;
- }
-
- if (n == 0)
- {
- break;
- }
-
- if (strncmp(recvbuffer,CFFAILEDSTR,strlen(CFFAILEDSTR)) == 0)
- {
- sprintf(OUTPUT,"Network access to %s:%s denied\n",ip->server,dirname);
- CfLog(cfinform,OUTPUT,"");
- return false;
- }
-
- if (strncmp(recvbuffer,"BAD:",4) == 0)
- {
- sprintf(OUTPUT,"%s\n",recvbuffer+4);
- CfLog(cfinform,OUTPUT,"");
- return false;
- }
-
- for (sp = recvbuffer; *sp != '\0'; sp++)
- {
- if (strncmp(sp,CFD_TERMINATOR,strlen(CFD_TERMINATOR)) == 0) /* End transmission */
- {
- cfdirh->cf_listpos = cfdirh->cf_list;
- return cfdirh;
- }
-
- AppendItem(&(cfdirh->cf_list),sp,NULL);
-
- while(*sp != '\0')
- {
- sp++;
- }
- }
- }
-
- cfdirh->cf_listpos = cfdirh->cf_list;
- return cfdirh;
- }
-
- /*********************************************************************/
-
- FlushClientCache(ip)
-
- struct Image *ip;
-
- {
- if (ip->cache)
- {
- free(ip->cache);
- ip->cache = NULL;
- }
- }
-
- /*********************************************************************/
-
- CompareMD5Net(file1,file2,ip)
-
- char *file1, *file2;
- struct Image *ip;
-
- { static unsigned char d[16];
- char sendbuffer[bufsize];
- char recvbuffer[bufsize],in[bufsize],out[bufsize];
- int i,offset;
-
- Debug("Send digest of %s to server\n",file2);
- cfMDFile(file2,d); /* send this to the server for comparison */
-
- bzero(recvbuffer,bufsize);
- bzero(sendbuffer,bufsize);
-
- if (20 + 3*16 + strlen(file1) > bufsize-1)
- {
- CfLog(cferror,"MD5 transfer overflow...updating for safety\n","");
- return true;
- }
-
-
- if (ip->secure)
- {
- #ifdef HAVE_LIBCRYPTO
- bzero(in,bufsize);
- bzero(out,bufsize);
- bzero(sendbuffer,bufsize);
- sprintf(in,"MD5 %s\n %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d \0",file1,
- d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8],d[9],d[10],d[11],d[12],d[13],d[14],d[15]);
- cfencrypt(in,out,CFDES1,CFDES2,CFDES3,strlen(in));
- sprintf(sendbuffer,"SMD5 %d",strlen(in));
- bcopy(out,sendbuffer+23,bufsize-24);
- #endif
- }
- else
- {
- sprintf(sendbuffer,"MD5 %s\n %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d \0",file1,
- d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8],d[9],d[10],d[11],d[12],d[13],d[14],d[15]);
- }
-
- if (send(SD,sendbuffer,bufsize,0) == -1)
- {
- CfLog(cferror,"","send");
- return false;
- }
-
- if (RecvSocketStream(SD,recvbuffer,bufsize,0) == -1)
- {
- Verbose("No answer from host, assuming checksum ok to avoid remote copy for now...\n");
- return false;
- }
-
- if (strcmp(CFD_TRUE,recvbuffer) == 0)
- {
- Debug("MD5 mismatch: %s\n",recvbuffer);
- return true; /* mismatch */
- }
- else
- {
- Debug("MD5 match: %s\n",recvbuffer);
- return false;
- }
-
- /* Not reached */
- }
-
- /*********************************************************************/
-
- CopyRegNet(source,new,ip,size)
-
- char *source, *new;
- struct Image *ip;
- off_t size;
-
- { int dd, buf_size, TimeOut(), i=0;
- char *buf,*cp,*sp,in[bufsize],out[bufsize],sendbuffer[bufsize],key[8];
- int n_read, *intp,err,toget,towrite,len;
- long n_read_total = 0;
- int last_write_made_hole = 0, done = false;
-
- Debug("CopyRegNet(%s,%s,%d) to socket %d\n",source,new,size,SD);
-
- if ((strlen(new) > bufsize-20))
- {
- CfLog(cferror,"Filename too long","");
- return false;
- }
-
- unlink(new); /* To avoid link attacks */
-
- if ((dd = open(new,O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0600)) == -1)
- {
- sprintf(OUTPUT,"Copy %s:%s security - failed attempt to exploit a race? (Not copied)\n",ip->server,new);
- CfLog(cferror,OUTPUT,"open");
- unlink(new);
- return false;
- }
-
- sendbuffer[0] = '\0';
-
- if (ip->secure)
- {
- #ifdef HAVE_LIBCRYPTO
- des_random_key(key);
- bzero(in,bufsize);
- bzero(out,bufsize);
- bzero(sendbuffer,bufsize);
- sprintf(in,"GET %8.8s %s",key,source);
- cfencrypt(in,out,CFDES1,CFDES2,CFDES3,strlen(in));
- sprintf(sendbuffer,"SGET %4d %4d",strlen(in),ST_BLKSIZE(dstat));
- bcopy(out,sendbuffer+31,bufsize-32);
- #endif
- }
- else
- {
- sprintf(sendbuffer,"GET %d %s",ST_BLKSIZE(dstat),source);
- }
-
- if (send(SD,sendbuffer,bufsize,0) == -1)
- {
- CfLog(cferror,"Couldn't send","send");
- close(dd);
- return false;
- }
-
- buf_size = ST_BLKSIZE(dstat);
- buf = (char *) malloc(bufsize + sizeof(int)); /* Note bufsize not buf_size */
-
- n_read_total = 0;
-
- while (!done)
- {
- if ((size - n_read_total)/buf_size > 0)
- {
- toget = towrite = buf_size;
- }
- else if (size != 0)
- {
- towrite = (size - n_read_total);
- toget = buf_size; /* padding required for encryption chaining */
- }
- else
- {
- toget = towrite = 0;
- }
-
- if ((n_read = RecvSocketStream(SD,buf,toget,0)) == -1)
- {
- if (errno == EINTR)
- {
- continue;
- }
-
- close(dd);
- free(buf);
- return false;
- }
-
- if (strncmp(buf,CFFAILEDSTR,strlen(CFFAILEDSTR)) == 0)
- {
- sprintf(OUTPUT,"Network access to %s:%s denied\n",ip->server,source);
- RecvSocketStream(SD,buf,bufsize-toget,0); /* flush rest of transaction */
- CfLog(cfinform,OUTPUT,"");
- close(dd);
- free(buf);
- return false;
- }
-
- if (strncmp(buf,"BAD:",4) == 0) /* error */
- {
- CfLog(cfinform,buf+4,"");
- close(dd);
- free(buf);
- return false;
- }
-
- if (ip->secure)
- {
- bzero(sendbuffer,bufsize);
- cfdecrypt(buf,sendbuffer,CFDES1,key,CFDES3,buf_size);
- bcopy(sendbuffer,buf,buf_size);
- }
-
- if (n_read == 0)
- {
- break;
- }
-
- n_read_total += towrite; /*n_read;*/
-
- if (n_read_total >= (long)size) /* Handle EOF without closing socket */
- { /* signature is 8 bytes */
- done = true;
- }
-
- intp = 0;
-
- if (ip->makeholes)
- {
- buf[n_read] = 1; /* Sentinel to stop loop. */
-
- /* Find first non-zero *word*, or the word with the sentinel. */
-
- intp = (int *) buf;
-
- while (*intp++ == 0)
- {
- }
-
- /* Find the first non-zero *byte*, or the sentinel. */
-
- cp = (char *) (intp - 1);
-
- while (*cp++ == 0)
- {
- }
-
- /* If we found the sentinel, the whole input block was zero,
- and we can make a hole. */
-
- if (cp > buf + n_read)
- {
- /* Make a hole. */
- if (lseek (dd,(off_t)n_read,SEEK_CUR) < 0L)
- {
- sprintf (OUTPUT,"lseek in CopyReg, dest=%s\n", new);
- CfLog(cferror,OUTPUT,"lseek");
- free(buf);
- unlink(new);
- close(dd);
- return false;
- }
- last_write_made_hole = 1;
- }
- else
- {
- /* Clear to indicate that a normal write is needed. */
- intp = 0;
- }
- }
-
- if (intp == 0)
- {
- if (cf_full_write (dd,buf,towrite) < 0)
- {
- sprintf(OUTPUT,"CopyReg(%s:%s,%s) failed\n",ip->server,source,new);
- CfLog(cferror,OUTPUT,"");
- close(dd);
- free(buf);
- unlink(new);
- return false;
- }
- last_write_made_hole = 0;
- }
- }
-
- /* If the file ends with a `hole', something needs to be written at
- the end. Otherwise the kernel would truncate the file at the end
- of the last write operation. */
-
- if (last_write_made_hole)
- {
- /* Write a null character and truncate it again. */
-
- if (cf_full_write (dd,"",1) < 0 || ftruncate (dd,n_read_total) < 0)
- {
- CfLog(cferror,"cfengine: full_write or ftruncate error in CopyReg\n","");
- free(buf);
- unlink(new);
- close(dd);
- return false;
- }
- }
-
- close(dd);
- free(buf);
- return true;
- }
-
- /*********************************************************************/
- /* Level 2 */
- /*********************************************************************/
-
- GetCachedStatData(file,statbuf,ip,stattype)
-
- char *file;
- struct stat *statbuf;
- struct Image *ip;
- char *stattype;
-
- { struct cfstat *sp;
-
- Debug("GetCachedStatData(%s)\n",file);
-
- for (sp = ip->cache; sp != NULL; sp=sp->next)
- {
- if ((strcmp(ip->server,sp->cf_server) == 0) && (strcmp(file,sp->cf_filename) == 0))
- {
- if (sp->cf_failed) /* cached failure from cfopendir */
- {
- errno = EPERM;
- Debug("Cached failure to stat\n");
- return -1;
- }
-
- if ((strcmp(stattype,"link") == 0) && (sp->cf_lmode != 0))
- {
- statbuf->st_mode = sp->cf_lmode;
- }
- else
- {
- statbuf->st_mode = sp->cf_mode;
- }
-
- statbuf->st_uid = sp->cf_uid;
- statbuf->st_gid = sp->cf_gid;
- statbuf->st_size = sp->cf_size;
- statbuf->st_atime = sp->cf_atime;
- statbuf->st_mtime = sp->cf_mtime;
- statbuf->st_ctime = sp->cf_ctime;
- statbuf->st_ino = sp->cf_ino;
- statbuf->st_nlink = sp->cf_nlink;
-
- Debug("Found in cache\n");
- return true;
- }
- }
-
- Debug("Did not find in cache\n");
- return false;
- }
-
- /*********************************************************************/
-
- CacheData(data,ip)
-
- struct cfstat *data;
- struct Image *ip;
-
- { struct cfstat *sp;
-
- if ((sp = (struct cfstat *) malloc(sizeof(struct cfstat))) == NULL)
- {
- CfLog(cferror,"Memory allocation faliure in CacheData()\n","");
- return;
- }
-
- bcopy(data,sp,sizeof(struct cfstat));
-
- sp->next = ip->cache;
- ip->cache = sp;
- }
-
-